Automode per core results - #1
Merged
privacywhen merged 5 commits intoAug 14, 2026
Merged
Conversation
…per-core results, continue after resume (sp00n#106) - Introduce a per-core state machine (pending/testing/confirmed/unstable/ignored) that drives all Automatic Test Mode scheduling; a core is confirmed after passesToConfirmCoreValue (new setting, default 3) consecutive error-free test runs at an unchanged value and is then never tested again, in-run or after a crash/resume - Append confirmed/unstable results immediately to a permanent, human-readable results file that survives crashes and reboots - In Automatic Test Mode the run now loops until every non-ignored core is resolved; maxIterations no longer governs the ATM loop (classic mode unchanged); an internal sanity limit catches pathological cores - Persist per-core states, iteration and the remaining core order in the .automode file (atomic writes, previous generation kept as backup) so an automatic resume continues the order where it left off instead of restarting from the beginning (fixes sp00n#106) - Add knownGoodValues setting to declare trusted per-core values that are never tested and reported as user-provided (AMD only) - Fix the false "All Cores have reached the maximum Curve Optimizer value" abort: duplicate-accepting error lists were counted against a de-duplicated core count - Harden the crash/resume machinery: the adjusted value of a crashed core is persisted before any voltage is applied to the CPU after a resume; terminal results from the results file are merged back on resume so a torn state write can never cause a confirmed core to be retested; a no-progress boot counter aborts a pathological crash-reboot loop (new exit code 7); the startup helper falls back to the backup state generation on any primary read/validation failure - Fix the alternate core order builder producing duplicate/incomplete orders for odd physical-core counts (even counts unchanged) - Fix pass detection dying permanently when a stress-test log is recreated smaller after a program restart; fix corepairs resume using $_ outside a pipeline - Per-core status row (OK/CFG/MAX) in the final summary; banner and exit code now reflect the actual outcome - Document everything in default.config.ini, the ATM example configs and readme.txt Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Convert-WheaMessageToApicId returns a negative value when the APIC ID cannot be determined: -1 for every event id other than 18 and 19, -2 when the entry has no Properties member at all. Convert-WheaMessageToCoreId then passed that value straight into the $coresInfo['apicIdToCore'] hash table. A key that doesn't exist returns $null, and [Int] $null is 0, so every WHEA entry that cannot be attributed to a core was silently reported as core 0. With treatWheaWarningAsError = 1 that had two consequences: - While core 0 was being tested, an unrelated WHEA entry (e.g. event id 47, "corrected machine check", component "Memory") was counted as a real error for core 0. - While any other core was being tested, the same entry printed "the APIC ID from the WHEA message does not match the tested core" with an APIC ID of -1 and the test continued as if nothing had happened. The same happened for a valid APIC ID that isn't in the map, which can occur on processors whose APIC IDs have a gap (e.g. a Ryzen 5900X). Convert-WheaMessageToCoreId now returns -1 in these cases instead, and the WHEA check treats an entry without a usable core as an error for the tested core, because it cannot be ruled out that it caused it. A negative APIC ID / core is displayed as "not available" instead of as a number. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ntil it has a final value Until now a core collected one confirmation pass per iteration, so with the default passesToConfirmCoreValue = 3 the whole test order had to be cycled through three times before the first core could be confirmed. On a 16 core processor that means the run has no final value for any core for a long time, and aborting it early leaves you with nothing. The new repeatCoreUntilConfirmed setting (default 1) re-inserts a core at the front of the test order after a successful test run, the same way repeatCoreOnError already does after an error, so the core is tested again right away. A core is therefore finished before the next one in line is started, and its value is written to the results file at that point. Setting it to 0 restores the previous behavior. The order in which the cores are selected is unchanged, it still comes from coreTestOrder. The loop index is decremented together with the re-insertion, so the number of test slots per iteration is unaffected and the persisted remaining core order stays correct for a resume. For Intel there is only a single voltage offset for all of the cores, so a core that is confirmed early keeps the value it was confirmed with even if a later core makes the shared value less aggressive. The Intel example config therefore sets this to 0 and the setting comment explains why. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…iteralPath and -Leaf
Split-Path's LiteralPathSet consists of -LiteralPath, -Resolve and
-Credential only. Combining -LiteralPath with -Leaf therefore cannot be
bound to any parameter set and throws
Parameter set cannot be resolved using the specified named parameters
Both call sites are only reached when resuming after a crash, which is
why this was not noticed earlier:
- Initialize-AutoModeResultsFile takes the else branch only when the
results file path was restored from the .automode file. It is not
inside a try/catch, so this aborted the whole run with a fatal error
on every single resume, which also removed the .automode file.
- The log file re-use in Import-Settings is inside a try/catch, so it
only lost the ability to continue writing to the log file of the
interrupted run and silently started a new one.
Both now use [System.IO.Path]::GetFileName(), which is a pure string
operation with no parameter sets and no wildcard handling, so it also
works for paths containing characters that Split-Path -Path would
interpret as wildcards.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automatic Test Mode: run until every core has a final value, persist per-core results, continue after resume (sp00n#106)
Replaces sp00n#181 — same branch, plus fixes for three issues found during live testing on a Ryzen 9 9950X3D (details at the bottom).
Problem
Running the Automatic Test Mode (e.g.
configs/Ryzen.AutomaticTestMode.Start.ini,startValues = Minimum) on a modern Ryzen frequently ends after effectively testing only the first core, reporting a Curve Optimizer value for that core alone. Two independent defects cause this:.automodefile only storeslastCoreTested+voltageValues— there is no record of which cores already finished. After each reboot the script rebuilds the full core order, restarts at iteration 1, and prepends the crashed core. With an aggressive start value that hard-freezes the machine (common at -50 on Zen 4/5), the run spends its whole life stepping the first core from -50 upward and never reaches the others.$coresWithErrorAndMaxVoltageValueis appended without a uniqueness check from both the in-loop error handler and the RESUME handler, then its.Countis compared to$numUniqueAvailableCores(and the compared value is stale — only refreshed inside the core loop). A single core pinned atmaxValueacross several crash/resume cycles inflates the counter until the script announces "All Cores have reached the maximum Curve Optimizer value and thrown an error, aborting!" while 15 cores were never tested.On top of that, the
finallyblock deletes.automodeon every exit, so any values that were found only ever existed in the console/log output.What this PR does
One new concept: a per-core state machine (
$coreStates:pending → testing → confirmed / unstable, plusignored), persisted across crashes, which drives all ATM scheduling:passesToConfirmCoreValue(new setting, default 3) consecutive error-free test runs at an unchanged value. Any error charged to the core bumps its value byincrementByand resets the counter.repeatCoreUntilConfirmed, new setting, default 1): after a successful test run the core is re-inserted at the front of the test order — the same mechanismrepeatCoreOnErroralready uses after an error — so its confirmation passes are collected back-to-back instead of one per full sweep of the core order. Aborting a run early therefore still leaves you with final values for the cores tested so far. Set to 0 to collect passes round-robin across the order instead (the Intel example config does, since a single voltage offset is shared by all cores there).maxValue). In ATM,maxIterationsno longer governs the loop (its default of 10000 made it meaningless there); classic-mode semantics are unchanged. Termination is provable: values increase monotonically towardmaxValue, and an internal sanity limit catches pathological cores..automodenow stores per-core states, the current iteration and the remaining core order (written atomically, previous generation kept as.automode-bakuntil the next good write). After a crash the crashed core is retested first (with its value bumped), then the order continues exactly where it left off; resolved cores are skipped.knownGoodValues(new setting, e.g.knownGoodValues = 3:-25, 7:-18): declare values you already trust from a previous run. Those cores are seeded as confirmed (source = config), never tested, and marked as user-provided in the summary and results file. The results file ends with a ready-to-pasteknownGoodValuesline. Fatal error on Intel (per-core values are an AMD concept).setVoltageOnlyForTestedCore = 0); terminal results are merged back from the results file on resume, so a torn state write can never lead to retesting a confirmed core; a persisted no-progress boot counter aborts a pathological crash-reboot loop after ~10 attempts (new exit code 7) and cleanly removes the scheduled task; the startup helper falls back to the backup state generation on any primary read/validation failure.WHEA errors without a usable APIC ID are no longer attributed to core 0
Convert-WheaMessageToApicIdreturns a negative value for any WHEA entry it cannot parse (any event id other than 18/19 — e.g. id 47, "corrected machine check", component Memory — or an entry without aPropertiesmember).Convert-WheaMessageToCoreIdfed that straight into the$coresInfo['apicIdToCore']hash table; a missing key returns$null, and[Int] $nullis0, so every unattributable WHEA entry was silently reported as core 0. WithtreatWheaWarningAsError = 1that meant a false error against core 0 whenever it was under test, and a silent "does not match the tested core" warning (APIC ID -1) for every other core — the test run then still counted as clean. The same applied to valid APIC IDs that are not in the map (possible on parts whose APIC IDs have gaps, e.g. a 5900X). Such entries are now treated as an error for the currently tested core — since no core can be ruled out, letting them pass silently would allow an unstable core to be confirmed — and the APIC ID / core are displayed as "not available" instead of a wrong number. (Found live: an event id 47 fired mid-test and was waved through.)Fixed along the way
alternatecore order builder produced duplicate/incomplete orders for odd physical-core counts (e.g. 5 cores →0,2,4,2,4, cores 1 and 3 never tested — also affects classic mode). It now covers all cores for any count; even counts produce the exact same order as before.Split-Path -LiteralPath ... -Leafcannot bind to any parameter set (LiteralPathSethas no-Leaf) and throws "Parameter set cannot be resolved" at runtime. Both call sites were on the crash-resume path only: one aborted every resume with a fatal error (which also deleted the.automodestate), the other silently prevented re-using the previous run's log file. Both now use[System.IO.Path]::GetFileName(). (Found live: the first actual crash-resume on the 9950X3D died on this.)ArrayListadds ($coresWithErrorAndMaxVoltageValue,$coresWithIncreasedVoltageValue) are now guarded.Get-NewLogfileEntriesno longer permanently disables pass detection if the log file is recreated smaller after a stress-program restart.corepairsresume:$_used outside a pipeline (always$nullunderSet-StrictMode 3.0).New settings (
[AutomaticTestMode])passesToConfirmCoreValue3repeatCoreUntilConfirmed10collects passes round-robin across the core orderknownGoodValues3:-25, 7:-18All are documented in
configs/default.config.iniandreadme.txt; the ATM example configs surface them.Compatibility
maxIterations, error handling, summary).knownGoodValuesis rejected with a clear fatal error; the Intel example config setsrepeatCoreUntilConfirmed = 0(one shared voltage offset — a core confirmed early would keep a value a later core then adjusts).treatWheaWarningAsError = 1strictly more conservative (entries that previously slipped through as core-0 mismatches now count); set it to 0 to keep WHEA entries advisory.Validation
[System.Management.Automation.Language.Parser]::ParseFileclean on both changed scripts.coresToIgnore), the banner/exit-code matrix, the resume flow incl. crash-attribution ordering, and the torn-save/resume-merge scenarios. Additional suites: WHEA attribution (16 assertions, incl. the live event id 47 case and a 5900X-style APIC gap), core scheduling withrepeatCoreUntilConfirmedon/off (20 assertions simulating the real loop mechanics incl. errors and duplicate orders), and the resume-only branches of the results-file initialization (23 assertions).Split-Pathfailure class); it flags the two old call sites on the pre-fix file and nothing on the fixed one.knownGoodValuesseeding confirmed working; the crash-resume cycle is what surfaced theSplit-Pathand WHEA findings above. A full end-to-end run is still outstanding.Closes sp00n#106.
🤖 Generated with Claude Code